This vignette exemplifies how to use Spatial Annotations in SPATA2. It builds on the spatial annotations created in the vignette Creating Spatial Annotations for the glioblastoma samples UKF313T and UKF275T.

library(SPATA2)
library(tidyverse)

# exchange my/path/to with your directory
object_t313 <- loadSpataObject("my/path/to/object_UKF313T.RDS")
object_t275 <- loadSpataObject("my/path/to/object_UKF275T.RDS")

1. Image subsetting

Spatial annotations annotate space which can be directly translated into the image regardless of the annotation was drawn based on histomorphological features (Image Annotations) or numeric and grouping features (Numeric- and GroupAnnotations). The annotated space can be used to obtain image sections cropped to only include the annotated area. getSpatialAnnotation() extracts an object of class SpatialAnnotation.

# obtain the IDs of all spatial annotations
getSpatAnnIds(object_t313)
## [1] "necrotic_area"   "necrotic_center" "necrotic_edge"   "necrotic_edge2"
necrotic_area <- 
  getSpatialAnnotation(
    object = object_t313,
    id = "necrotic_area", 
    add_image = T
    )

# print summary 
necrotic_area
## An object of class 'ImageAnnotation' with id = 'necrotic_area'. 
## Tags: necrotic.
# print slot names
slotNames(necrotic_area)
## [1] "parent_name" "area"        "id"          "image"       "image_info"  "misc"        "sample"      "tags"        "version"

By default, the image is cropped in a way that only the annotation is included.

# visualize from the SPATA2 object, with expand = 0
plotSpatialAnnotations(object_t313, ids = "necrotic_area", expand = 0, fill = NA)

# expand = 0 is how the image is extracted when extracting the spatial annotation
# (in SPATA2, images are plotted upside down to fit into the cartesian coordinate system)
plot(EBImage::flip(necrotic_area@image))
Fig.1 The image annotation 'necrotic_area' extracted as is.Fig.1 The image annotation 'necrotic_area' extracted as is.

Fig.1 The image annotation ‘necrotic_area’ extracted as is.

The argument expand can be used with getSpatialAnnotation(), too, in order to manipulate with how much extra space the image is cropped. This works in pixel as well as with SPATA2’s SI unit system.

# visualize from the SPATA2 object, with expand = 0
plotSpatialAnnotations(
  object = object_t313,
  ids = "necrotic_area", 
  expand = "500um", 
  fill = NA, 
  sb_dist = "500um", 
  sb_pos = c("4.5mm", "3mm")
  )

necrotic_area_expanded <- 
  getSpatialAnnotation(
    object = object_t313,
    id = "necrotic_area", 
    add_image = T, 
    expand = "500um"
    )

expanded_image <- EBImage::flip(necrotic_area_expanded@image)

plot(expanded_image)
Fig.2 The image annotation 'necrotic_area' extracted with a 500um buffer at each side.Fig.2 The image annotation 'necrotic_area' extracted with a 500um buffer at each side.

Fig.2 The image annotation ‘necrotic_area’ extracted with a 500um buffer at each side.

2. Inferring expression gradients

The borders of spatial annotations can be used as reference points to analyze expression of numeric features (e.g. gene expression) as a function of distance to the annotated areas. The figure below illustrates the concept.

necrotic_ids <- c("necrotic_area", "necrotic_edge", "necrotic_edge2")

# obtain distance data relative to spatial annotations (SA) with getCoordsDfSA()
coords_df <- 
  getCoordsDfSA(object_t313, ids = necrotic_ids, unit = "mm", binwidth = "200um", core0 = T) 

# add distance to necrosis as meta feature to the SPATA2 object
# to make them accessible for other functions
object_t313 <- 
  addFeatures(object_t313, feature_df = coords_df[,c("barcodes", "dist", "bins_dist")], overwrite = T)

# create ggproto layer for further surface plots
necrotic_outline <- 
  ggpLayerSpatAnnOutline(object_t313, ids = necrotic_ids, incl_edge = T, fill = "grey")

sas_screning <- 
  ggpLayerScreeningDirectionSAS(object_t313, ids = necrotic_ids, line_size = 0.75)

# show plots 
plotImage(object_t313, outline = T) + 
  necrotic_outline + 
  sas_screning

plotSurface(object_t313, color_by = "dist", pt_clrsp = "plasma") + 
  necrotic_outline + 
  sas_screning + 
  labs(color = "Dist [mm]")
Fig. 3 Concept of illustrating expression as a function of distance.Fig. 3 Concept of illustrating expression as a function of distance.

Fig. 3 Concept of illustrating expression as a function of distance.

Inferring expression as function of distance is part of the Spatial Gradient Screening algorithm (SAS). Therefore, functions related to this concept feature the acronym Sas such as plotSasLineplot() or plotSasHeatmap().

plotSurface(object_t313, color_by = "HM_HYPOXIA", display_image = F, outline = T, pt_clrsp = "BuPu") + 
  necrotic_outline + 
  sas_screning

plotSasLineplot(object_t313, ids = necrotic_ids, variables = "HM_HYPOXIA", line_color = "blue") + 
  labs(x = "Distance to Necrotic Borders [mm]")
Fig.4 Visualizing HM_HYPOXIA expression as a function of distance to necrosis.Fig.4 Visualizing HM_HYPOXIA expression as a function of distance to necrosis.

Fig.4 Visualizing HM_HYPOXIA expression as a function of distance to necrosis.

If the number of features displayed becomes too high for a lineplot switch to plotSasHeatmap().

# use spatialAnnotationScreening() to identify non random genes 
# and genes with specific expression pattern related to your spatial annotations
associated_with_necrosis <-
  c("SLC2A1", "ADM", "ERO1A", "CD44", "STC2", "CA12", "VEGFA", "NDRG1", "TMEM158", "LOX")

repelled_by_necrosis <- 
  c("CD74", "CLU", "IFITM3", "C1QA", "C1QB", "CD68", "SELENOP", "SEC61B", "HLA-DRB1", "EMC6")

plotSasHeatmap(object_t313, variables = associated_with_necrosis, ids = necrotic_ids, clrsp = "BuPu") + 
  labs(x = "Distance to Necrotic Borders [mm]")

plotSasHeatmap(object_t313, variables = repelled_by_necrosis, ids = necrotic_ids, clrsp = "Reds 3") + 
  labs(x = "Distance to Necrotic Borders [mm]")
Fig.5 Visualizing gene expression as a function of distance via heatmaps.Fig.5 Visualizing gene expression as a function of distance via heatmaps.

Fig.5 Visualizing gene expression as a function of distance via heatmaps.

3. Core, Environment and Periphery

The concept of Spatial Annotation Screening differentiates three types of areas when working with spatial annotations:

By default distance is the maximum of distances between the objects observations (here, barcoded spots) to their respective closest annotation border. In the case of the three necrotic annotations the maximum distance is approximately 2mm.

## in mm, since unit = "mm"
round(max(coords_df$dist), digits = 2)
## [1] 1.99

This is referred to as distance to edge (dte) as observations up to the tissue’s edge are included. If your hypothesis requires a specific distance parameter up to which the expression gradient is inferred use the distance parameter. Fig.6 visualizes the differences in the set up.

sas_areas <- color_vector(clrp = "npg", names = c("core", "environment", "periphery"))

necrotic_outline_transp <- 
  ggpLayerSpatAnnOutline(object_t313, ids = necrotic_ids, incl_edge = T, fill = NA)

# rel_loc = location of each observation relative to the set up
plotSurface(coords_df, color_by = "rel_loc", clrp_adjust = sas_areas) + 
  necrotic_outline_transp +
  labs(subtitle = "`distance` = 'dte'")

# the scenario changes when distance is specified
coords_df2 <- 
  getCoordsDfSA(object_t313, ids = necrotic_ids, distance = "1.5mm")

plotSurface(coords_df2, color_by = "rel_loc", clrp_adjust = sas_areas) + 
  necrotic_outline_transp +
  labs(subtitle = "`distance` = 1.5mm")
Fig.7 Different set ups depending on the `distance` parameter.Fig.7 Different set ups depending on the `distance` parameter.

Fig.7 Different set ups depending on the distance parameter.

Another aspect to consider is the core parameter. Depending on whether it is set to core = TRUE or core = FALSE the inside of the annotation is included in the gradient. It depends on the circumstances and your hypothesis whether it makes sense to include the core or not. In case of the necrotic areas we decided to set core = FALSE since we were particularly interested in the reaction of the surrounding of the necrotic areas. In other cases core = TRUE is suitable:

object_t275 <- 
  createNumericAnnotations(
    object = object_t275, 
    variable = "HM_HYPOXIA", 
    threshold = "kmeans_high", 
    id = "hypoxic_ann", 
    overwrite = TRUE
  )

signatures <- c("HM_HYPOXIA", "RCTM_TCR_SIGNALING")

horizon_layer <- 
  ggpLayerHorizonSAS(
    object = object_t275,
    id = "hypoxic_ann_1", 
    distance = "1.5mm", 
    line_size = 1, 
    line_type = "dotted"
    )

hypoxic_outline <- 
  ggpLayerSpatAnnOutline(
    object = object_t275, 
    ids = "hypoxic_ann_1", 
    line_size = 1.25
  )

plotSurfaceComparison(object_t275, color_by = signatures, pt_clrsp = "red", normalize = T, display_image = F, ncol = 1) + 
  hypoxic_outline + 
  horizon_layer + 
  # use SI axes
  ggpLayerAxesSI(object_t275, unit = "mm", breaks = str_c(c(2, 4, 6), "mm"))

plotSasLineplot(
  object = object_t275,
  variables = rev(signatures), 
  distance = "1.5mm", 
  core = TRUE, 
  line_color = "red", 
  line_size = 1.5,
  border_linealpha = 1, 
  border_linecolor = "black",
  border_linetype = "solid", 
  ncol = 1
  ) + 
  geom_vline(xintercept = 1.5, linetype = "dotted") + 
  labs(x = "Distance to Hypoxic Border [mm]")
Fig.8 An example where the inclusion of the core makes sense.Fig.8 An example where the inclusion of the core makes sense.

Fig.8 An example where the inclusion of the core makes sense.